﻿using System.Collections.Generic;
using HtmlAgilityPack;
using RssReaderTemplate.Model.TagProcessors;

namespace RssReaderTemplate.Model
{
    public class ModelBuilder
    {
        private string _itemHtml;
        private int _isBold;
        private int _isItalic;
        private IDictionary<string, object> _context = new Dictionary<string, object>();
        public ModelBuilder(string itemHtml)
        {
            _itemHtml = itemHtml;
        }

        public IEnumerable<ContentFragment> BuildModel()
        {
            HtmlDocument document = new HtmlDocument();
            document.LoadHtml(_itemHtml);
            return MergeTextFragments(BuildModel(document.DocumentNode));
        }

        private IEnumerable<ContentFragment> MergeTextFragments(IEnumerable<ContentFragment> iEnumerable)
        {
            for (IEnumerator<ContentFragment> i = iEnumerable.GetEnumerator(); i.MoveNext(); )
            {
                var current = i.Current as TextFragment;
                if (current != null)
                {
                    var aggregated = new AggregatedTextFragments();
                    bool hasNext;
                    bool previousIsLineFeed = false;
                    do
                    {
                        if (!(previousIsLineFeed && (current.InnerText == " " || current.InnerText == "\n")))
                        {
                            aggregated.TextFragments.Add(current);
                        }
                        previousIsLineFeed = current is LineFeedFragment;
                        hasNext = i.MoveNext();
                        current = i.Current as TextFragment;
                    } while (hasNext && current != null);
                    yield return aggregated;
                    if (hasNext)
                    {
                        yield return i.Current;
                    }
                }
                else
                {
                    yield return i.Current;
                }
            }
            yield break;
        }

        private IEnumerable<ContentFragment> BuildModel(HtmlNode node)
        {
            TagProcessor processor = TagProcessorCollection.GetProcessor(node.Name);
            if (processor != null)
            {
                var pre = processor.PreProcessTag(node,_context);
                if (pre != null)
                {
                    yield return pre;
                }
            }
            var descendands = node.ChildNodes;
            foreach (var child in descendands)
            {
                foreach (var childFragment in BuildModel(child))
                {
                    yield return childFragment;
                }
            }
            if (processor != null)
            {
                var post = processor.PostProcessTag(node,_context);
                if (post != null)
                {
                    yield return post;
                }
            }
        }

        private ContentFragment PreProcessTag(HtmlNode node)
        {
            switch (node.Name)
            {
                case "img":
                    return new ImageFragment(node.Attributes["src"].Value);
                case "a":
                    var link = new LinkFragment(
                        node.Attributes["href"].Value,
                        node.InnerText, _isBold > 0, _isItalic > 0
                        );
                    node.RemoveAllChildren();
                    return link;
                case "strong":
                    _isBold++;
                    break;
                case "i":
                    _isItalic++;
                    break;
                case "br":
                    return new LineFeedFragment();
                case "#text":
                    return new TextFragment(node.InnerText, _isBold > 0, _isItalic > 0);
                case "script":
                case "object":
                case "table":
                    node.RemoveAllChildren();
                    break;
                default:
                    break;
            }
            return null;
        }

        private ContentFragment PostProcessTag(HtmlNode node)
        {
            switch (node.Name)
            {
                case "p":
                case "h1":
                case "h2":
                case "h3":
                case "h4":
                case "h5":
                case "h6":
                case "h7":
                    return new LineFeedFragment();
                case "strong":
                    _isBold--;
                    break;
                case "i":
                    _isItalic--;
                    break;
                default:
                    break;
            }
            return null;
        }

    }

}
